
#pragma comment( lib, "glew32.lib" )
//#pragma comment( lib, "freeglut.lib" )
//#pragma comment( lib, "opengl32.lib" )

// Using sprintf instead of the "correct" c++ version ostringstream
#define _CRT_SECURE_NO_DEPRECATE

#define CUBE_MAP_SIZE 256

#include <GL/glew.h>
#include <GL/freeglut.h> // include freeglut before stdlib

#include <stdlib.h>
#include <stdio.h>

#include <time.h>

#include <cmath>
#include <iostream>
#include <vector>

#include "imageloader.h"
#include "pastycam.h"

GLuint texture1;
GLuint ffCubeMap;
GLuint 

enum {CUBE_POS_X, CUBE_NEG_X, CUBE_POS_Y, CUBE_NEG_Y, CUBE_POS_Z, CUBE_NEG_Z};
static unsigned char CubeMap[6][CUBE_MAP_SIZE][CUBE_MAP_SIZE][4];

class rgba
{
public:
	unsigned char r,g,b,a;
	rgba(){r=0; g=0; b=0; a=0;} // default
	rgba(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha) 
		: r(red), g(green), b(blue), a(alpha) {} 
};

using std::vector;

const int KEY_ID_A = 0;
const int KEY_ID_B = 1;
const int KEY_ID_C = 2;
const int KEY_ID_D = 3;
const int KEY_ID_E = 4;
const int KEY_ID_F = 5;
const int KEY_ID_G = 6;
const int KEY_ID_H = 7;
const int KEY_ID_I = 8;
const int KEY_ID_J = 9;
const int KEY_ID_K = 10;
const int KEY_ID_L = 11;
const int KEY_ID_M = 12;
const int KEY_ID_N = 13;
const int KEY_ID_O = 14;
const int KEY_ID_P = 15;
const int KEY_ID_Q = 16;
const int KEY_ID_R = 17;
const int KEY_ID_S = 18;
const int KEY_ID_T = 19;
const int KEY_ID_U = 20;
const int KEY_ID_V = 21;
const int KEY_ID_W = 22;
const int KEY_ID_X = 23;
const int KEY_ID_Y = 24;
const int KEY_ID_Z = 25;
const int MOUSE_LEFT_BUTTON_DOWN = 26;
const int MOUSE_RIGHT_BUTTON_DOWN = 27;
const int MOUSE_MIDDLE_BUTTON_DOWN = 28;
const int KEY_ID_LEFT = 29;
const int KEY_ID_RIGHT = 30;
const int KEY_ID_UP = 31;
const int KEY_ID_DOWN = 32;


namespace global
{
  double lastframetime;
  int window;

  bool keyPressed[200]; // mebe 109 is max. 200 to be save.
  int mouseX, mouseY; 
  int oldMouseX, oldMouseY; 
  int resX, resY;
  PastyCam cam;
  double time;
  
  int fps;
  int frames;
  double fpsTimer;
}

GLuint EmptyTexture()							// Create An Empty Texture
{
	GLuint txtnumber;						// Texture ID
	unsigned int* data;						// Stored Data

	// Create Storage Space For Texture Data (128x128x4)
	data = (unsigned int*)new GLuint[((128 * 128)* 4 * sizeof(unsigned int))];
	ZeroMemory(data,((128 * 128)* 4 * sizeof(unsigned int)));	// Clear Storage Memory

	glGenTextures(1, &txtnumber);					// Create 1 Texture
	glBindTexture(GL_TEXTURE_2D, txtnumber);			// Bind The Texture
	glTexImage2D(GL_TEXTURE_2D, 0, 4, 128, 128, 0,
		GL_RGBA, GL_UNSIGNED_BYTE, data);			// Build Texture Using Information In data
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	delete [] data;							// Release data

	return txtnumber;						// Return The Texture ID
}


vector< vector<rgba> > loadPixels(const std::string& filename, 
				 bool flipY)
{
		std::vector<unsigned char> image;
		unsigned long width, height;
		int error = ImageLoader::loadImagePNG(image, width, height, filename);
		
    vector< vector<rgba> > pixels;
    
    //if there's an error, display it
		if(error != 0)
		{
		}
		else
		{
			std::cout << "PicoPNG loaded image \t" << filename.c_str() << " \t " << width << "x" <<  height << std::endl;
      pixels = 
				vector< vector<rgba> >( height, vector<rgba>
															( width, rgba(255,0,0,0) ) );

			
			
      // Pack pixels into RGBA and flip Y
      // Read pixeldata stream into a 2x2 array for easier access.
      size_t w = width-1;
      size_t h = height-1;
      size_t i = 0;
      for(size_t y=0; y<height; y++)
      {
        for(size_t x=0; x<width; x++)
        {
          unsigned char r = image[i++];
          unsigned char g = image[i++];
          unsigned char b = image[i++];
          unsigned char a = image[i++];
          
          if( flipY ) pixels[h-y][x] = rgba( r,g,b,a );
          else pixels[y][x] = rgba( r,g,b,a );
          
        }
      }
			
    }
    return pixels;
}

void loadTexture(const std::string& filename, 
				 GLuint texHandle, 
				 bool flipY,
				 bool filtering)
{
		std::vector<unsigned char> image;
		unsigned long width, height;
		int error = ImageLoader::loadImagePNG(image, width, height, filename);
		//if there's an error, display it
		if(error != 0)
		{
		}
		else
		{
			std::cout << "PicoPNG loaded image \t" << filename.c_str() << " \t " << width << "x" <<  height << std::endl;
		
			if( flipY )
			{
				// Pack pixels into RGBA and flip Y
				vector< vector<rgba> > pixels = 
					vector< vector<rgba> >( height, vector<rgba>
															( width, rgba(255,0,0,0) ) );

				// Read pixeldata stream into a 2x2 array for easier access.
				size_t w = width-1;
				size_t h = height-1;
				size_t i = 0;
				for(size_t y=0; y<height; y++)
				{
					for(size_t x=0; x<width; x++)
					{
						unsigned char r = image[i++];
						unsigned char g = image[i++];
						unsigned char b = image[i++];
						unsigned char a = image[i++];
						pixels[y][x] = rgba( r,g,b,a );
					}
				}

				//size_t h = height-1;
				// How im used to dealing with pixel data:
				//pixels[0][0].r = 255;
				//pixels[0][0].g = 0;
				//pixels[0][0].b = 0;

				i = 0;
				// Read into image with y coord flipped as OpenGL expects.
				for(size_t y=0; y<height; y++)
				{
					for(size_t x=0; x<width; x++)
					{
						image[i++] = pixels[h-y][x].r;
						image[i++] = pixels[h-y][x].g;
						image[i++] = pixels[h-y][x].b;
						image[i++] = pixels[h-y][x].a;
					}
				}
			}
      
      glBindTexture(GL_TEXTURE_2D, texHandle);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &image[0] );
      
			if(filtering)
			{
				glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
				glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			}
			else
			{
				glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
				glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
			}

			//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // GL_REPEAT,GL_CLAMP, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER
			//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
      
		}
}

/*
void setCubemapPixels(int f, vector< vector<rgba> > pixels)
{
  size_t width = pixels.size();
  size_t height = pixels[0].size();
  std::cout << width;
  std::cout << height;
  
  for(size_t y=0; y<height; y++)
  {
    for(size_t x=0; x<width; x++)
    {
      face[f][x][y][0] = pixels[x][y].r;
      face[f][x][y][1] = pixels[x][y].g;
      face[f][x][y][2] = pixels[x][y].b;
    }
  }
}
*/

/*
void loadCubeMap()
{
    for (int i=0; i<6; i++) {
      glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT + i,
        0,                  //level
        GL_RGB8,            //internal format
        64,                 //width
        64,                 //height
        0,                  //border
        GL_RGB,             //format
        GL_UNSIGNED_BYTE,   //type
        &face[i][0][0][0]); // pixel data
    }
}
*/

static float
Dot3(float *a, float *b)
{
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}

static float *
Scale3(float *result, float *a, float scale)
{
  result[0] = a[0] * scale;
  result[1] = a[1] * scale;
  result[2] = a[2] * scale;
  return result;
}

static float *
Normalize3(float *result, float *a)
{
  float length;

  length = (float) sqrt(Dot3(a, a));
  return Scale3(result, a, 1 / length);
}

static unsigned char *
CubeFunc(unsigned char resultColor[3], float vec[3])
{
  int i;
  float faceVec[3];

  if (vec[0] == 1.0) {
    resultColor[0] = 255;
    resultColor[1] = 0;
    resultColor[2] = 0;
  } else if (vec[1] == 1.0) {
    resultColor[0] = 0;
    resultColor[1] = 255;
    resultColor[2] = 0;
  } else if (vec[2] == 1.0) {
    resultColor[0] = 0;
    resultColor[1] = 0;
    resultColor[2] = 255;
  } else if (vec[0] == -1.0) {
    resultColor[0] = 255;
    resultColor[1] = 0;
    resultColor[2] = 255;
  } else if (vec[1] == -1.0) {
    resultColor[0] = 255;
    resultColor[1] = 255;
    resultColor[2] = 0;
  } else if (vec[2] == -1.0) {
    resultColor[0] = 0;
    resultColor[1] = 255;
    resultColor[2] = 255;
  }
  return resultColor;

  Normalize3(faceVec, vec);
  for (i = 0; i < 3; i++) {
    resultColor[i] = 255 * (sin(6 * (faceVec[i] + faceVec[(i + 1) % 3])) + 1) / 2.0;
  }
  return resultColor;
}

GLenum cubefaces[6] = {
  GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT,
  GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT,
  GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT,
  GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT,
  GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT,
  GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT,
};


void enableCubeMap()
{

  for (int i = 0; i < CUBE_MAP_SIZE; i++) {
    float t = 1.0 / (2 * CUBE_MAP_SIZE) + (float) i / CUBE_MAP_SIZE;
    t = 2.0 * t - 1.0;
    for (int j = 0; j < CUBE_MAP_SIZE; j++) {
      float s = 1.0 / (2 * CUBE_MAP_SIZE) + (float) j / CUBE_MAP_SIZE;
      float pt[3];
      s = 2.0 * s - 1.0;
      pt[0] = 1;
      pt[1] = t;
      pt[2] = -s;
      CubeFunc(CubeMap[CUBE_POS_X][i][j], pt);
      pt[0] = -1;
      pt[1] = t;
      pt[2] = s;
      CubeFunc(CubeMap[CUBE_NEG_X][i][j], pt);

      pt[1] = 1;
      pt[0] = s;
      pt[2] = -t;
      CubeFunc(CubeMap[CUBE_POS_Y][i][j], pt);
      pt[1] = -1;
      pt[0] = s;
      pt[2] = t;
      CubeFunc(CubeMap[CUBE_NEG_Y][i][j], pt);

      pt[2] = 1;
      pt[0] = s;
      pt[1] = t;
      CubeFunc(CubeMap[CUBE_POS_Z][i][j], pt);
      pt[2] = -1;
      pt[0] = -s;
      pt[1] = t;
      CubeFunc(CubeMap[CUBE_NEG_Z][i][j], pt);
      for (int k = CUBE_POS_X; k <= CUBE_NEG_Z; k++) {
        CubeMap[k][i][j][3] = 255;
      }
    }
  }

  glEnable(GL_DEPTH_TEST);

  
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

  glGenTextures(1, &ffCubeMap);
  glBindTexture(GL_TEXTURE_CUBE_MAP, ffCubeMap );

  for (int i = 0; i < 6; i++) {
    glTexImage2D(
      cubefaces[i],
      0,                  // level
      GL_RGBA8,          // internal format
      CUBE_MAP_SIZE,     // width
      CUBE_MAP_SIZE,     // height
      0,                 // border
      GL_RGBA,           // format
      GL_UNSIGNED_BYTE,   // type
      CubeMap[CUBE_POS_X + i]); // pixel data
  }

  glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

  glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT);
  glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT);
  glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT);
  glEnable(GL_TEXTURE_CUBE_MAP_EXT);
  glEnable(GL_TEXTURE_GEN_S);
  glEnable(GL_TEXTURE_GEN_T);
  glEnable(GL_TEXTURE_GEN_R);
  glEnable(GL_NORMALIZE);
}

void sleep(int ms)
{
  Sleep(ms);
}

double currentTime()
{
	// <time>'s GetTickCount is choppy. Low resolution.
  //double dTime = GetTickCount() / 1000.0;
  double dTime = float(glutGet(GLUT_ELAPSED_TIME)) / 1000.0;
  return (dTime);
}

float lerp(float x1, float x2, float t){
	return (x2-x1)*t;
}

float rnd()
{
  return float( rand() ) / float(RAND_MAX-1);
}

void renderBitmapString(float x, float y, float z, void *font, char *string) {  
	char *c;
  glRasterPos3f(x, y,z);
  for (c=string; *c != '\0'; c++) {
    glutBitmapCharacter(font, *c);
  }
}


void setOrthographicProjection() {

	// switch to projection mode
	glMatrixMode(GL_PROJECTION);
	// save previous matrix which contains the 
	//settings for the perspective projection
	glPushMatrix();
	// reset matrix
	glLoadIdentity();
	// set a 2D orthographic projection
	gluOrtho2D(0, global::resX, 0, global::resY);
	// invert the y axis, down is positive
	glScalef(1, -1, 1);
	// mover the origin from the bottom left corner
	// to the upper left corner
	glTranslatef(0, -GLfloat(global::resY), 0);
	glMatrixMode(GL_MODELVIEW);
}

void resetPerspectiveProjection() {
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
}


void setupGlew()
{
	GLenum err = glewInit();
	if (GLEW_OK != err)
	{
	  /* Problem: glewInit failed, something is seriously wrong. */
	  fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
	  exit(0);
	}
	fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
}

void loadTextures()
{
 glGenTextures(1, &texture1);
 loadTexture("hello.png", texture1, false, false);
 
 
 //setCubemapPixels(0, loadPixels("hello.png",false) );
}

void init()
{
  glClearColor(0.0, 0.0, 0.0, 0.0);
  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);

  global::lastframetime = currentTime();
  global::fpsTimer = 0;
  global::fps = 0;
  global::frames = 0;
  
  loadTextures();
  enableCubeMap();
}

void drawGrid()
{
  glBegin(GL_LINES);
	for(int i=-50; i<50; i++)
	{
		glVertex3f((float)i,0,50.0f);
		glVertex3f((float)i+1,0,-50.0f);

		glVertex3f(50.0f,0,(float)i);
		glVertex3f(-50.0f,0,(float)i+1.0f);
	}
  glEnd();
}

void drawText()
{
  setOrthographicProjection();
  glLoadIdentity();

  char buf[100];
  float x = 5.0f;
  float y = 15.0f;
  for(int i=0; i<26; i++)
  {
  	char c = (char)i+65;
	sprintf(buf, "%c", c);
  	if( global::keyPressed[i] )
  	{
  		glColor3ub(255,0,0);
  	}else{
  		glColor3ub(255,255,255);
  	}
  	
	renderBitmapString(x,y,0,GLUT_BITMAP_HELVETICA_18, buf );
  	y += 15.0f;
  	if( y >= float(global::resY) )
  	{
  		y = 15.0f;
  		x += 150;
  	}	
  }
  
  if( global::keyPressed[KEY_ID_LEFT] )
  {
  	sprintf(buf, "<-");
  	renderBitmapString( (float)global::resX/2-15, (float)global::resY/2,0,GLUT_BITMAP_HELVETICA_18,buf);
  }
  if( global::keyPressed[KEY_ID_RIGHT] )
  {
  	sprintf(buf, "->");
  	renderBitmapString( (float)global::resX/2+15, (float)global::resY/2,0,GLUT_BITMAP_HELVETICA_18,buf);
  }
  if( global::keyPressed[KEY_ID_UP] )
  {
  	sprintf(buf, "/|\\");
  	renderBitmapString( (float)global::resX/2, (float)global::resY/2-10,0,GLUT_BITMAP_HELVETICA_18,buf);
  }
  if( global::keyPressed[KEY_ID_DOWN] )
  {
  	sprintf(buf, "\\|/");
  	renderBitmapString( (float)global::resX/2, (float)global::resY/2+10,0,GLUT_BITMAP_HELVETICA_18,buf);
  }
  
  sprintf(buf, "%i fps", global::fps);
  renderBitmapString( (float)global::resX-100, (float)15,0,GLUT_BITMAP_HELVETICA_18,buf);
  
  resetPerspectiveProjection();
}

void calcFps()
{
  if( global::time > global::fpsTimer )
  {
    global::fps = global::frames;
    global::frames = 0;
    global::fpsTimer = global::time + 1.0;
  }
  global::frames++;
}

void display()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  global::time = currentTime();
  float dt = (float)global::time - (float)global::lastframetime;
  global::lastframetime = global::time;
  
  float mouseSpeedX, mouseSpeedY;
  if(global::keyPressed[MOUSE_LEFT_BUTTON_DOWN])
  {
  	float mouseSpd = 0.01f;
  	mouseSpeedX = mouseSpd*float(global::mouseX-global::oldMouseX);
    mouseSpeedY = mouseSpd*float(global::mouseY-global::oldMouseY);
  }
  else
  {
    mouseSpeedX = 0;
    mouseSpeedY = 0;
  }
  global::oldMouseX = global::mouseX;
  global::oldMouseY = global::mouseY;
  
  float f = 5.0f;
  float vx = f * float(global::keyPressed[KEY_ID_LEFT] - global::keyPressed[KEY_ID_RIGHT]) * dt;
  float vz = f * float(global::keyPressed[KEY_ID_UP] - global::keyPressed[KEY_ID_DOWN]) * dt;
  global::cam.update( -vx,
  			  0.0f,
  			  -vz,
  			  -mouseSpeedX,
  			  mouseSpeedY
  );
  
  glLoadMatrixd( global::cam.getMatrix() );
  
  glPushMatrix();
  glTranslatef(0,-10,0);
  drawGrid();
  glPopMatrix();
  
  glPushMatrix();
  glTranslatef(0,0,-30);
  glEnable(GL_TEXTURE_CUBE_MAP_EXT);
  //glEnable(GL_TEXTURE_2D);
  //glBindTexture(GL_TEXTURE_2D, texture1);
  //    glEnable(GL_TEXTURE_CUBE_MAP_EXT);
    
  glRotatef(100.0f*global::time, 0, 1, 0);
  glutSolidTeapot(5);
  //glEnable(GL_TEXTURE_CUBE_MAP_EXT);
  
  glDisable(GL_TEXTURE_CUBE_MAP_EXT);
  //glDisable(GL_TEXTURE_2D);
  
  glPopMatrix();
  
  
  drawText();
  calcFps();
  
  glutSwapBuffers();
  glutPostRedisplay();

}

void keyDown(unsigned char key, int x, int y)
{
	switch (key){
	case 'a': global::keyPressed[KEY_ID_A] = true; break;
	case 'b': global::keyPressed[KEY_ID_B] = true; break;
	case 'c': global::keyPressed[KEY_ID_C] = true; break;
	case 'd': global::keyPressed[KEY_ID_D] = true; break;
	case 'e': global::keyPressed[KEY_ID_E] = true; break;
	case 'f': global::keyPressed[KEY_ID_F] = true; break;
	case 'g': global::keyPressed[KEY_ID_G] = true; break;
	case 'h': global::keyPressed[KEY_ID_H] = true; break;
	case 'i': global::keyPressed[KEY_ID_I] = true; break;
	case 'j': global::keyPressed[KEY_ID_J] = true; break;
	case 'k': global::keyPressed[KEY_ID_K] = true; break;
	case 'l': global::keyPressed[KEY_ID_L] = true; break;
	case 'm': global::keyPressed[KEY_ID_M] = true; break;
	case 'n': global::keyPressed[KEY_ID_N] = true; break;
	case 'o': global::keyPressed[KEY_ID_O] = true; break;
	case 'p': global::keyPressed[KEY_ID_P] = true; break;
	case 'q': global::keyPressed[KEY_ID_Q] = true;  
		glutDestroyWindow(global::window); 
		#ifndef _WIN32 
		// Must use this with regular glut, since it never returns control to main().
		exit(0); 
		#endif
	break;
	case 'r': global::keyPressed[KEY_ID_R] = true; break;
	case 's': global::keyPressed[KEY_ID_S] = true; break;
	case 't': global::keyPressed[KEY_ID_T] = true; break;
	case 'u': global::keyPressed[KEY_ID_U] = true; break;
	case 'v': global::keyPressed[KEY_ID_V] = true; break;
	case 'w': global::keyPressed[KEY_ID_W] = true; break;
	case 'x': global::keyPressed[KEY_ID_X] = true; break;
	case 'y': global::keyPressed[KEY_ID_Y] = true; break;
	case 'z': global::keyPressed[KEY_ID_Z] = true; break;
	}
}

void keyUp(unsigned char key, int x, int y)
{
	switch (key){
	case 'a': global::keyPressed[KEY_ID_A] = false; break;
	case 'b': global::keyPressed[KEY_ID_B] = false; break;
	case 'c': global::keyPressed[KEY_ID_C] = false; break;
	case 'd': global::keyPressed[KEY_ID_D] = false; break;
	case 'e': global::keyPressed[KEY_ID_E] = false; break;
	case 'f': global::keyPressed[KEY_ID_F] = false; break;
	case 'g': global::keyPressed[KEY_ID_G] = false; break;
	case 'h': global::keyPressed[KEY_ID_H] = false; break;
	case 'i': global::keyPressed[KEY_ID_I] = false; break;
	case 'j': global::keyPressed[KEY_ID_J] = false; break;
	case 'k': global::keyPressed[KEY_ID_K] = false; break;
	case 'l': global::keyPressed[KEY_ID_L] = false; break;
	case 'm': global::keyPressed[KEY_ID_M] = false; break;
	case 'n': global::keyPressed[KEY_ID_N] = false; break;
	case 'o': global::keyPressed[KEY_ID_O] = false; break;
	case 'p': global::keyPressed[KEY_ID_P] = false; break;
	case 'q': global::keyPressed[KEY_ID_Q] = false; break;
	case 'r': global::keyPressed[KEY_ID_R] = false; break;
	case 's': global::keyPressed[KEY_ID_S] = false; break;
	case 't': global::keyPressed[KEY_ID_T] = false; break;
	case 'u': global::keyPressed[KEY_ID_U] = false; break;
	case 'v': global::keyPressed[KEY_ID_V] = false; break;
	case 'w': global::keyPressed[KEY_ID_W] = false; break;
	case 'x': global::keyPressed[KEY_ID_X] = false; break;
	case 'y': global::keyPressed[KEY_ID_Y] = false; break;
	case 'z': global::keyPressed[KEY_ID_Z] = false; break;
	}
}

void specialKeyUp(int key,int x,int y)
{
  switch(key)
  {
    case  GLUT_KEY_F1                        : break;
    case  GLUT_KEY_F2                        : break;
    case  GLUT_KEY_F3                        : break;
    case  GLUT_KEY_F4                        : break;
    case  GLUT_KEY_F5                        : break;
    case  GLUT_KEY_F6                        : break;
    case  GLUT_KEY_F7                        : break;
    case  GLUT_KEY_F8                        : break;
    case  GLUT_KEY_F9                        : break;
    case  GLUT_KEY_F10                       : break;
    case  GLUT_KEY_F11                       : break;
    case  GLUT_KEY_F12                       : break;
    case  GLUT_KEY_LEFT                      : global::keyPressed[KEY_ID_LEFT] = false; break;
    case  GLUT_KEY_UP                        : global::keyPressed[KEY_ID_UP] = false; break;
    case  GLUT_KEY_RIGHT                     : global::keyPressed[KEY_ID_RIGHT] = false; break;
    case  GLUT_KEY_DOWN                      : global::keyPressed[KEY_ID_DOWN] = false; break;
    case  GLUT_KEY_PAGE_UP                   : break;
    case  GLUT_KEY_PAGE_DOWN                 : break;
    case  GLUT_KEY_HOME                      : break;
    case  GLUT_KEY_END                       : break;
    case  GLUT_KEY_INSERT                    : break;                
  }
}

void specialKeyDown(int key,int x,int y)
{
  switch(key)
  {
    case  GLUT_KEY_F1                        : break;
    case  GLUT_KEY_F2                        : break;
    case  GLUT_KEY_F3                        : break;
    case  GLUT_KEY_F4                        : break;
    case  GLUT_KEY_F5                        : break;
    case  GLUT_KEY_F6                        : break;
    case  GLUT_KEY_F7                        : break;
    case  GLUT_KEY_F8                        : break;
    case  GLUT_KEY_F9                        : break;
    case  GLUT_KEY_F10                       : break;
    case  GLUT_KEY_F11                       : break;
    case  GLUT_KEY_F12                       : break;
    case  GLUT_KEY_LEFT                      : global::keyPressed[KEY_ID_LEFT] = true; break;
    case  GLUT_KEY_UP                        : global::keyPressed[KEY_ID_UP] = true; break;
    case  GLUT_KEY_RIGHT                     : global::keyPressed[KEY_ID_RIGHT] = true; break;
    case  GLUT_KEY_DOWN                      : global::keyPressed[KEY_ID_DOWN] = true; break;
    case  GLUT_KEY_PAGE_UP                   : break;
    case  GLUT_KEY_PAGE_DOWN                 : break;
    case  GLUT_KEY_HOME                      : break;
    case  GLUT_KEY_END                       : break;
    case  GLUT_KEY_INSERT                    : break;                
  }
}

void mousePressed(int button, int state, int posX, int posY)
{
	global::mouseX = posX;
	global::mouseY = posY;
	if(state == GLUT_DOWN)
	{
		switch (button){
		case  GLUT_LEFT_BUTTON: global::keyPressed[MOUSE_LEFT_BUTTON_DOWN] = true; break;    
		case  GLUT_RIGHT_BUTTON: global::keyPressed[MOUSE_RIGHT_BUTTON_DOWN] = true; break;    
		case  GLUT_MIDDLE_BUTTON: global::keyPressed[MOUSE_MIDDLE_BUTTON_DOWN] = true; break;    
		}
	}
	else if(state == GLUT_UP){
		switch (button){
		case  GLUT_LEFT_BUTTON: global::keyPressed[MOUSE_LEFT_BUTTON_DOWN] = false; break;    
		case  GLUT_RIGHT_BUTTON: global::keyPressed[MOUSE_RIGHT_BUTTON_DOWN] = false; break;    
		case  GLUT_MIDDLE_BUTTON: global::keyPressed[MOUSE_MIDDLE_BUTTON_DOWN] = false; break;    
		}	
	}
}

void mouseMoved(int posX, int posY)
{
	global::mouseX = posX;
	global::mouseY = posY;
}

void mousePassive(int posX, int posY)
{
	global::mouseX = posX;
	global::mouseY = posY;
}

void reshape(int w, int h)
{
	global::resX = w;
	global::resY = h;
  glViewport(0, 0, (GLsizei) w, (GLsizei) h); 
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(60.0f, float(w)/float(h) ,1.0f, 3000.0f);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

int main(int argc, char** argv)
//int _tmain(int argc, _TCHAR* argv[])
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
  global::resX = 700;
  global::resY = 700;
  glutInitWindowSize(global::resX, global::resY); 
  glutInitWindowPosition(10, 10);
  global::window = glutCreateWindow("Simple FreeGlut Window");
  init();
  setupGlew();
  glutKeyboardFunc(keyDown);
  glutKeyboardUpFunc(keyUp);
  
  glutSpecialFunc(specialKeyDown); // special keys are F1-F12, arrowkeys
  glutSpecialUpFunc(specialKeyUp);
  
  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutMouseFunc(mousePressed);
  glutMotionFunc(mouseMoved);
  glutPassiveMotionFunc(mousePassive);

  // Add other callback functions here..

  glutMainLoop();
  return 0;
}

